﻿Imports Microsoft.Practices.EnterpriseLibrary.Data
Imports System.Data
Imports System.Net
Imports System.Configuration.ConfigurationManager
Imports System.Security.Permissions

<Assembly: CLSCompliant(True)> 

''' <summary>
''' The base <see cref="Exception">Exception</see> object to be used by .NET applications.
''' </summary>
''' <revisions>
'''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
''' </revisions>
<Serializable()> Public MustInherit Class BaseException
  Inherits Exception

#Region " Variable Declarations"

  Private m_DateTime As DateTime = Now.ToUniversalTime
  Private m_Severity As ExceptionSeverity = ExceptionSeverity.Information
  Private m_HasExceptionBeenLogged As Boolean
  Private m_HasNotificationBeenSent As Boolean
  Private m_ClassName As String
  Private m_MethodName As String
  Private m_AssemblyInformation As Microsoft.VisualBasic.ApplicationServices.AssemblyInfo
  Private m_ExceptionID As String
  Private m_System As String

#End Region

#Region " Constructors"

  ''' <summary>
  ''' Initializes a new instance of the <see cref="BaseException">BaseException</see> class.
  ''' </summary>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Protected Sub New()
    MyBase.New()
  End Sub

  ''' <summary>
  ''' Initializes a new instance of the <see cref="BaseException">BaseException</see> class.
  ''' </summary>
  ''' <param name="assemblyInformation">Information on the assembly where the exception was thrown.</param>
  ''' <param name="className">Name of the class where the exception was thrown.</param>
  ''' <param name="methodName">Name of the method where the exception was thrown.</param>
  ''' <param name="message">A message the describes the error..</param>
  ''' <param name="severity">The <see cref="severity">severity</see> of the exception.</param>
  ''' <param name="logException"><see cref="TriState">TriState</see> flag indicating whether to log the exception.  If <b>UseDefault</b> is specified, the determination will be made based upon the configuration settings.</param>
  ''' <param name="sendNotification"><see cref="TriState">TriState</see> flag indicating whether to send a notification to the application administrator (as defined in the configuration settings).  If <b>UseDefault</b> is specified, the determination will be made based upon the configuration settings.</param>
  ''' <remarks>The content of the <paramref name="Message" /> parameter is intended to be understood by humans.  The caller of this constructor is required to ensure that this string has been localized for the current system culture.</remarks>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Protected Sub New(ByVal assemblyInformation As Microsoft.VisualBasic.ApplicationServices.AssemblyInfo, _
                    ByVal className As String, _
                    ByVal methodName As String, _
                    ByVal message As String, _
                    ByVal severity As ExceptionSeverity, _
                    ByVal logException As TriState, _
                    ByVal sendNotification As TriState)
    MyBase.New(message)
    Me.InitializeClass(assemblyInformation, className, methodName, severity, logException, sendNotification)
  End Sub

  ''' <summary>
  ''' Initializes a new instance of the <see cref="BaseException">BaseException</see> class with a reference to the inner exception that is the cause of this exception.
  ''' </summary>
  ''' <param name="assemblyInformation">The assembly information.</param>
  ''' <param name="className">Name of the class.</param>
  ''' <param name="methodName">Name of the method.</param>
  ''' <param name="message">The message.</param>
  ''' <param name="innerException">The exception that is the cause of the current exception.  If the <paramref name="InnerException">InnerException</paramref> parameter is not a null reference (Nothing in Visual Basic), the current exception is raised in a <b>catch</b> block that handles the inner exception.</param>
  ''' <param name="severity">The severity.</param>
  ''' <param name="logException">The log exception.</param>
  ''' <param name="sendNotification">The send notification.</param>
  ''' <remarks>
  '''   <para>
  '''     The content of the <paramref name="Message" /> parameter is intended to be understood by humans.
  '''     The caller of this constructor is required to ensure that this string has been localized for the
  '''     current system culture.
  '''   </para>
  '''   <para>
  '''     An exception that is thrown as a direct result of a previous exception should include a
  '''     reference to the previous exception in the <see cref="InnerException" /> property.  The
  '''     <b>InnerException</b> property returns the same value that is passed into the constructor,
  '''     or a null reference if the <b>InnerException</b> property does not supply the inner exception
  '''     value to the constructor.
  '''   </para>
  ''' </remarks>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Protected Sub New(ByVal assemblyInformation As Microsoft.VisualBasic.ApplicationServices.AssemblyInfo, _
                    ByVal className As String, _
                    ByVal methodName As String, _
                    ByVal message As String, _
                    ByVal innerException As Exception, _
                    ByVal severity As ExceptionSeverity, _
                    ByVal logException As TriState, _
                    ByVal sendNotification As TriState)
    MyBase.New(message, innerException)
    Me.InitializeClass(assemblyInformation, className, methodName, severity, logException, sendNotification)
  End Sub

  ''' <summary>
  ''' Initializes a new instance of the <see cref="BaseException">BaseException</see> class.
  ''' </summary>
  ''' <param name="info">The info.</param>
  ''' <param name="context">The context.</param>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Protected Sub New(ByVal info As System.Runtime.Serialization.SerializationInfo, _
                ByVal context As System.Runtime.Serialization.StreamingContext)
    MyBase.New(info, context)
  End Sub

#End Region

#Region " Property Values"

  ''' <summary>
  ''' Gets the identifier of the exception.
  ''' </summary>
  ''' <value>The identifier of the exception.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property Id() As String
    Get
      Return m_ExceptionID
    End Get
  End Property

  ''' <summary>
  ''' Gets the name of the system that generated the exception.
  ''' </summary>
  ''' <value>The name of the system that generated the exception.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property System() As String
    Get
      Return m_System
    End Get
  End Property

  ''' <summary>
  ''' Gets the <see cref="DateTime">DateTime</see> the exception was raised.
  ''' </summary>
  ''' <value>The <see cref="DateTime">DateTime</see> the exception was raised.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property DateTime() As DateTime
    Get
      Return m_DateTime
    End Get
  End Property

  ''' <summary>
  ''' Gets the <see cref="severity">severity</see> of the exception.
  ''' </summary>
  ''' <value>The <see cref="severity">severity</see> of the exception.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property Severity() As ExceptionSeverity
    Get
      Return m_Severity
    End Get
  End Property

  ''' <summary>
  ''' Gets a flag indicating whether the exception has been logged.
  ''' </summary>
  ''' <value><c>True</c> if the exception has been logged; otherwise, <c>False</c>.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property ExceptionLogged() As Boolean
    Get
      Return m_HasExceptionBeenLogged
    End Get
  End Property

  ''' <summary>
  ''' Gets a flag indicating whether a notification has been sent to the application administrator.
  ''' </summary>
  ''' <value><c>True</c> if a notification has been sent; otherwise, <c>False</c>.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property NotificationSent() As Boolean
    Get
      Return m_HasNotificationBeenSent
    End Get
  End Property

  ''' <summary>
  ''' Gets the name of the class where the exception was raised.
  ''' </summary>
  ''' <value>The name of the class where the exception was raised.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property ClassName() As String
    Get
      Return m_ClassName
    End Get
  End Property

  ''' <summary>
  ''' Gets the name of the method where the exception was raised.
  ''' </summary>
  ''' <returns>The name of the method where the exception was raised.</returns>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property MethodName() As String
    Get
      Return m_MethodName
    End Get
  End Property

  ''' <summary>
  ''' Gets the <see cref="Microsoft.VisualBasic.ApplicationServices.AssemblyInfo">AssemblyInfo</see> for the assembly that threw the exception.
  ''' </summary>
  ''' <value>The <see cref="Microsoft.VisualBasic.ApplicationServices.AssemblyInfo">AssemblyInfo</see> for the assembly that threw the exception.</value>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Public ReadOnly Property AssemblyInformation() As Microsoft.VisualBasic.ApplicationServices.AssemblyInfo
    Get
      Return m_AssemblyInformation
    End Get
  End Property

#End Region

#Region " Public Methods"

  ''' <summary>
  ''' When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
  ''' </summary>
  ''' <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
  ''' <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
  ''' <exception cref="T:System.ArgumentNullException">The <paramref name="info" /> parameter is a null reference (Nothing in Visual Basic). </exception>
  ''' <PermissionSet>
  ''' <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*" />
  ''' <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter" />
  ''' </PermissionSet>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  <SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags:=SecurityPermissionFlag.SerializationFormatter)> _
  Public Overrides Sub GetObjectData(ByVal info As System.Runtime.Serialization.SerializationInfo, ByVal context As System.Runtime.Serialization.StreamingContext)
    MyBase.GetObjectData(info, context)
  End Sub

#End Region

#Region " Private Utility Methods"

  ''' <summary>
  ''' Initializes the property values and performs other actions required to initialize the class.
  ''' </summary>
  ''' <param name="assemblyInformation">The assembly information.</param>
  ''' <param name="className">The name of the class where the exception occurred.</param>
  ''' <param name="methodName">The name of the method where the exception occurred.</param>
  ''' <param name="severity">The severity of the exception.</param>
  ''' <param name="logException"><see cref="TriState" /> flag indicating whether to log the exception.  If <b>UseDefault</b> is specified, the determination will be made based upon the configuration settings.</param>
  ''' <param name="sendNotification"><see cref="TriState" /> flag indicating whether to send a notification to the application administrator (as defined in the configuration settings).  If <b>UseDefault</b> is specified, the determination will be made based upon the configuration settings.</param>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Private Sub InitializeClass(ByVal assemblyInformation As Microsoft.VisualBasic.ApplicationServices.AssemblyInfo, _
                              ByVal className As String, _
                              ByVal methodName As String, _
                              ByVal severity As ExceptionSeverity, _
                              ByVal logException As TriState, _
                              ByVal sendNotification As TriState)

    ' Variable Declarations
    Dim logExceptionFlag As Boolean
    Dim sendNotificationFlag As Boolean

    ' Set the property values
    m_System = AppSettings.Get("ExceptionSystem")
    m_ExceptionID = GenerateExceptionID()
    m_AssemblyInformation = assemblyInformation
    m_ClassName = className
    m_MethodName = methodName
    m_Severity = severity

    ' Log the exception (if necessary)
    Select Case logException
      Case TriState.True
        logExceptionFlag = True
      Case TriState.False
        logExceptionFlag = False
      Case TriState.UseDefault
        logExceptionFlag = GetExceptionLoggingFlag(severity)
    End Select
    If logExceptionFlag Then
      m_HasExceptionBeenLogged = Me.LogException
    Else
      m_HasExceptionBeenLogged = False
    End If

    ' Send the notification (if necessary)
    Select Case sendNotification
      Case TriState.True
        sendNotificationFlag = True
      Case TriState.False
        sendNotificationFlag = False
      Case TriState.UseDefault
        sendNotificationFlag = GetNotificationFlag(severity)
    End Select
    If sendNotificationFlag Then
      m_HasNotificationBeenSent = Me.SendNotification
    Else
      m_HasNotificationBeenSent = False
    End If

  End Sub

  ''' <summary>
  ''' Logs the exception.
  ''' </summary>
  ''' <remarks><c>True</c> if the method successfully logs the exception in the database; otherwise, <c>False</c>.</remarks>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  '''   <revision date="02/21/2009" author="CEG" version="1.0.0.0">Corrected for the updated call to procCreateAssemblyInfo.</revision>
  '''   <revision date="02/21/2009" author="CEG" version="1.0.0.0">Changed to a function to return an indication as to whether the exception was logged.</revision>
  ''' </revisions>
  <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification:="We purposely ignore exceptions thrown at that point.")> _
  Private Function LogException() As Boolean

    ' Variable Declarations
    Dim returnValue As Boolean

    ' Log the exception into the database
    Try

      ' Variable Declarations
      Dim exceptionTrackerDatabase As Database = DatabaseFactory.CreateDatabase("ExceptionTracker")
      Dim execptionSource As String
      Dim stackTrace As String
      Dim targetSite As String
      Dim assemblyInfoId As Integer

      ' Get the exception parameters
      If IsNothing(Me.Source) = False Then
        execptionSource = Me.Source
      Else
        execptionSource = ""
      End If
      If IsNothing(Me.StackTrace) = False Then
        stackTrace = Me.StackTrace
      Else
        stackTrace = ""
      End If
      If IsNothing(Me.TargetSite) = False Then
        targetSite = Me.TargetSite.ToString
      Else
        targetSite = ""
      End If

      ' Store the general exception information
      exceptionTrackerDatabase.ExecuteNonQuery("procCreateException", Me.Id, _
                                                                      Me.System, _
                                                                      Me.Message, _
                                                                      execptionSource, _
                                                                      stackTrace, _
                                                                      targetSite, _
                                                                      Me.DateTime, _
                                                                      Me.Severity.ToInt16, _
                                                                      Me.NotificationSent, _
                                                                      Me.ClassName, _
                                                                      Me.MethodName)

      ' Store the assembly information
      exceptionTrackerDatabase.ExecuteNonQuery("procCreateAssemblyInfo", Me.Id, _
                                                                         Me.AssemblyInformation.AssemblyName, _
                                                                         Me.AssemblyInformation.CompanyName, _
                                                                         Me.AssemblyInformation.Copyright, _
                                                                         Me.AssemblyInformation.Description, _
                                                                         Me.AssemblyInformation.DirectoryPath, _
                                                                         Me.AssemblyInformation.ProductName, _
                                                                         Me.AssemblyInformation.StackTrace, _
                                                                         Me.AssemblyInformation.Title, _
                                                                         Me.AssemblyInformation.Trademark, _
                                                                         Me.AssemblyInformation.Version.ToString, _
                                                                         Me.AssemblyInformation.WorkingSet, _
                                                                         assemblyInfoId)
      If assemblyInfoId > 0 Then
        For LoadedAssemblyIndex As Integer = 0 To Me.AssemblyInformation.LoadedAssemblies.Count - 1
          Try
            exceptionTrackerDatabase.ExecuteNonQuery("procCreateLoadedAssembly", assemblyInfoId, _
                                                                                            Me.AssemblyInformation.LoadedAssemblies.Item(LoadedAssemblyIndex).FullName, _
                                                                                            Me.AssemblyInformation.LoadedAssemblies.Item(LoadedAssemblyIndex).Location)
          Catch exLoadedAssembly As Exception
            ' Ignore the exception
          End Try
        Next LoadedAssemblyIndex
      End If

      ' Store the inner exceptions
      If IsNothing(Me.InnerException) = False Then SaveInnerException(exceptionTrackerDatabase, 0, InnerException)

      ' Set up the return value
      returnValue = True

    Catch ex As Exception
      returnValue = False
    End Try

    ' Return the results
    Return returnValue

  End Function

  ''' <summary>
  ''' Saves the inner exception.
  ''' </summary>
  ''' <param name="exceptionTrackerDatabase">The exception tracker database.</param>
  ''' <param name="parentId">Identifier of the parent exception.</param>
  ''' <param name="innerException">The inner exception to be stored.</param>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification:="We purposely ignore exceptions thrown at that point.")> _
  Private Sub SaveInnerException(ByVal exceptionTrackerDatabase As Database, _
                                 ByVal parentId As Integer, _
                                 ByVal innerException As System.Exception)
    Try

      ' Variable Declarations
      Dim source As String
      Dim stackTrace As String
      Dim targetSite As String

      ' Determine the values to be stored
      If IsNothing(innerException.Source) = False Then
        source = innerException.Source
      Else
        source = ""
      End If
      If IsNothing(innerException.StackTrace) = False Then
        stackTrace = innerException.StackTrace
      Else
        stackTrace = ""
      End If
      If IsNothing(innerException.TargetSite) = False Then
        targetSite = innerException.TargetSite.ToString
      Else
        targetSite = ""
      End If

      ' Save the inner exception
      If IsNothing(innerException.InnerException) = False Then
        Dim dsInnerException As DataSet = exceptionTrackerDatabase.ExecuteDataSet("procCreateInnerException", parentId, _
                                                                                                              Me.Id, _
                                                                                                              innerException.Message, _
                                                                                                              source, _
                                                                                                              stackTrace, _
                                                                                                              targetSite)
        If dsInnerException.HasData Then SaveInnerException(exceptionTrackerDatabase, Convert.ToInt32(dsInnerException.Tables(0).Rows(0).Item(0)), innerException.InnerException)
      Else
        exceptionTrackerDatabase.ExecuteNonQuery("procCreateInnerException", parentId, _
                                                                             Me.Id, _
                                                                             innerException.Message, _
                                                                             source, _
                                                                             stackTrace, _
                                                                             targetSite)
      End If
    Catch ex As Exception
      ' Do nothing
    End Try
  End Sub

  ''' <summary>
  ''' Sends the notification.
  ''' </summary>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  '''   <revision date="02/21/2009" author="CEG" version="1.0.0.0">Changed to a function to return an indication as to whether the exception was logged.</revision>
  ''' </revisions>
  <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification:="We purposely ignore exceptions thrown at that point.")> _
  Private Function SendNotification() As Boolean

    ' Variable Declarations
    Dim returnValue As Boolean

    ' Sent the exception notification
    Try
      Dim smtpClient As New Mail.SmtpClient
      smtpClient.Send(AppSettings.Get("ExceptionNotificationFrom"), _
                      AppSettings.Get("ExceptionNotificationTo"), _
                      AppSettings.Get("ExceptionNotificationSubject"), _
                      "Exception ID " & Me.Id & " was thrown by " & Me.System & " with the following message:  " & Me.Message)
      returnValue = True
    Catch ex As Exception
      returnValue = False
    End Try

    ' Return the results
    Return returnValue

  End Function

  ''' <summary>
  ''' Gets the exception logging flag.
  ''' </summary>
  ''' <param name="Severity">The severity.</param>
  ''' <returns></returns>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification:="We purposely ignore exceptions thrown at that point.")> _
  Private Shared Function GetExceptionLoggingFlag(ByVal Severity As ExceptionSeverity) As Boolean

    ' Variable Declarations
    Dim configKey As String

    ' Get the Configuration Key
    Select Case Severity
      Case ExceptionSeverity.Critical
        configKey = "ExceptionLoggingCritical"
      Case ExceptionSeverity.Warning
        configKey = "ExceptionLoggingWarning"
      Case ExceptionSeverity.Validation
        configKey = "ExceptionLoggingValidation"
      Case ExceptionSeverity.Information
        configKey = "ExceptionLoggingInformation"
      Case Else
        Return False
    End Select

    ' Read the configuration to determine whether or not we need to log the exception
    If IsNothing(AppSettings.Get(configKey)) = False Then
      Try
        Return AppSettings.Get(configKey).ToBoolean
      Catch ex As Exception
        Return False
      End Try
    Else
      Return False
    End If

  End Function

  ''' <summary>
  ''' Gets the notification flag.
  ''' </summary>
  ''' <param name="Severity">The severity.</param>
  ''' <returns></returns>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  <System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification:="We purposely ignore exceptions thrown at that point.")> _
  Private Shared Function GetNotificationFlag(ByVal Severity As ExceptionSeverity) As Boolean

    ' Variable Declarations
    Dim configKey As String

    ' Get the Configuration Key
    Select Case Severity
      Case ExceptionSeverity.Critical
        configKey = "ExceptionNotificationCritical"
      Case ExceptionSeverity.Warning
        configKey = "ExceptionNotificationWarning"
      Case ExceptionSeverity.Validation
        configKey = "ExceptionNotificationValidation"
      Case ExceptionSeverity.Information
        configKey = "ExceptionNotificationInformation"
      Case Else
        Return False
    End Select

    ' Read the configuration to determine whether or not we need to send the notification
    If IsNothing(AppSettings.Get(configKey)) = False Then
      Try
        Return AppSettings.Get(configKey).ToBoolean
      Catch ex As Exception
        Return False
      End Try
    Else
      Return False
    End If

  End Function

  ''' <summary>
  ''' Generates the identifier for the exception.
  ''' </summary>
  ''' <returns>The identifier for the exception.</returns>
  ''' <revisions>
  '''   <revision date="12/14/2008" author="CEG" version="1.0.0.0">Initial Development</revision>
  ''' </revisions>
  Private Shared Function GenerateExceptionID() As String

    ' Variable Declarations
    Dim computerName As String = My.Computer.Name.Substring(My.Computer.Name.Length - 3, 3)
    Dim ticks As String = Now.Ticks.ToString.PadRight(18, Convert.ToChar("0"))

    ' Ensure the computer name is not greater than 11 characters
    If computerName.Length > 11 Then computerName = computerName.Substring(computerName.Length - 12, 11)

    ' Return the results
    Return computerName & "-" & ticks.Substring(0, 4) & "-" & ticks.Substring(4, 4) & "-" & ticks.Substring(8, 4) & "-" & ticks.Substring(12, 4) & "-" & ticks.Substring(16) & (Int((99 * Rnd()) + 1)).ToString

  End Function

#End Region

End Class